<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vue笔记</title>
</head>

<body>
  <script>
    VUE脚手架安装
      npm i vue - cli - g

    VUE项目创建
      vue init webpack[name]
      name: 项目名字

    VUE实例 API
      new Vue({
        el: '', //字符串，挂载元素
        template: '', //字符串，挂载组件字符串
        components: [], //数组，注册组件
        router: {}, //对象实例，路由
        name: '', //字符串，组件名字
        data: {}, //对象，数据
        created: function () {}, //函数，钩子       组件加载前钩子函数   在渲染组件的时候调用一次，再次复用组件不会调用
        watch: {}, //对象，观察属性     key为需要监控的属性   val为监控属性的回调
        computed: {}, //计算属性        需要使用2个或以上的属性进行计算时使用    key为计算结果的属性   val为计算公式回调  每当需要参与计算的属性改变时，执行回调
      })

    VUE常用目录结构
      assets //存放css,img,js等
      components //存放公共组件
      lib //存放第三方库，方法
      router //存放路由
      views //存放视图组件

    vue - router API
      挂载: Vue.use(VueRouter);
    vue路由实例 API
      var router = new VueRouter({
        mode: 'history', //路由模式
        linkActiveClass: 'active-link', //路由激活时router-link ClassName
        linkExactActiveClass: 'exact-active-link', // 路由路径完全匹配时激活的class
        // 在浏览器不支持history路由时，vue自动处理为hash路由，为false表示不处理
        fallback: true,
        //点击浏览器前进后退的时候或切换路由是触发   记录滚动条的坐标
        scrollBehavior(to, from, savedPosition) {
          console.log(to); //要进入的路由对象    目标路由
          console.log(from); //离开的路由对象    从哪里来
          console.log(savedPosition); //记录滚动条的坐标  点击前进后退的时候记录触发

          //路由跳转滚动条定位 
          if (savedPosition) {
            return savedPosition //如果有记录坐标，返回坐标值
          } else {
            return {
              x: 0,
              y: 0
            } //如果没有记录坐标，返回最顶部（0）
          }

          //路由跳转元素定位 
          if (to.hash) { //根据目标的路由hash，定位到想要的元素
            return {
              selected: to.hash, //给定位目标的元素上加和hash一样的id
            }
          }
        },
        routes: [ //路由 
          {
            path: '/', //路径
            path: '/user/:userId?', //:后面的为动态路由   ?为最少没有，最多一个，同正则
            component: [name], //组件  name:组件变量
            name: '', //路由名字
            redirect: (to) => { //动态设置重定向目标
              return '/' //重定向到根路径
              console.log(to); //目标路由对象，就是访问的路径的路由信息
              console.log(to.path); //为目标路由路径
            },
            alias: '/baobao', //路由别名，访问这个路径也能到这个路由
            children: [ //子路由
              {
                path: '', //子路由地址,如果为''的话是默认子路由
                component: [name] //子路由组件    name:组件变量                          
              }
            ],
            components: { //  如果一个路由下有多个组件的同时显示,使用components而不使用components
              default: [name], //默认显示的组件  name:组件名
              [name]: [component] //在有name属性的router-view上显示组件   name:router-view的name名  component:组件名
            },
            meta: { //路由元信息，自定义数据
              index: 0, //如设置路由的索引值
            },
            beforeEnter: (to, from, next) => { //进入当前路由后触发的钩子函数   ***单路由钩子
              console.log(to); //当前导航路由对象        到哪里去
              console.log(from); //从哪来导航路由对象      从哪里来
              next(); //默认使用这个钩子拦截导航，如果需要进入目标路由，则执行next函数
            },
            // 路由传递参数，在子组件中通过props接收  reutrn一个fun
            props: () => {{}}  
          },
          {
            path: '*', //通配符，匹配上面匹配不到的全部路由
            component: [name], //路由组件
            redirect: '/', //重定向到路径到根
          }
        ],
      })

    router.app //指向vue根实例   路由实例对象上的app属性指向vue的根实例

    $router //router实例对象
    this.$router.push('/') //跳转到首页

    $route
    //当前激活的路由信息对象，每个组件实例都有，地址一旦发生变化，会重新生成一个路由对象
    this.$route
    参数：
      $route = {
        fullPath: '', //字符串，路由完整路径包括hash和seach
        hash: '', //字符串，路由的hash
        name: '', //字符串，路由的name
        matched: [], //数组，包含当前路由的所有嵌套路径片段的路由记录
        params: {}, //对象，当前路由的动态参数
        query: {}, //对象，查询字符串    使用url seach传入的参数都在query这个对象中，也可以使用这个对象传参
        matched: [] //数组，自己和父级路由对象信息
      }

    beforeEach
    //router实例的钩子函数，在进入每个路由前执行   ***全局路由钩子  使用router实例调用
    //使用方法：
    router.beforeEach((to, from, next) => {
      console.log(to); //目标导航路由对象       到哪里去
      console.log(from); //从哪来导航路由对象     从哪里来
      next(); //默认使用这个钩子拦截导航，如果需要进入目标路由，则执行next函数
      next('/user') //使用next方法接收一个路径的参数，重定向到参数的路径

      //案例
      if (to.meta.login) { //判断目标组件是否登录
        next('/user') //没有登录重定向到登录组件
      } else {
        next() //登录后直接进入组件
      }
    })

    afterEach
    //router实力的钩子函数，在进入每个路由后执行    ***全局路由钩子
    //使用方法：
    router.afterEach((to, from) => {
      //可以利用这个钩子改变每个页面的title
      console.log(to); //到哪里去
      console.log(from); //从哪里来

      //案例
      if (to.meta.title) { //判断有没有自定义的title
        window.document.title = to.meta.title; //如果有的话修改为自定义的title
      } else {
        window.document.title = 'softLink'; //如果没有的话走默认的title
      }
    })

    beforeRouteEnter
    //进入组件前的钩子   在组件中配置   ***单路由钩子  写在组件vue实例中
    beforeRouteEnter((to, from, next) => {
      console.log(to); //当前导航路由对象       到哪里去
      console.log(from); //从哪来导航路由对象     从哪里来
      next(); //默认使用这个钩子拦截导航，如果需要进入目标路由，则执行next函数
      //在路由组件调用的时候vue实例还没有创建，所以this不会指向vue实例
      next((vm) => { //在next中加一个回调函数，接受一个参数，这个参数就是指向vue的实例对象
        console.log(vm); //vue的实例对象
      })
    })

    beforeRouteUpdate
    //组件中路径更新的时候会调用这个钩子函数   ***单路由钩子
    beforeRouteUpdate(to, from, next) {
      console.log(to); //当前二级导航路由对象         到哪里去
      console.log(from); //从哪来二级导航路由对象       从哪里来
      next(); //默认使用这个钩子拦截导航，如果需要进入目标路由，则执行next函数
    }
    beforeRouteLeave
    //离开组件前的钩子   ***单路由钩子
    beforeRouteLeave((to, from, next) => {
      console.log(to); //当前导航路由对象     到哪里去
      console.log(from); //从哪来导航路由对象   从哪里来
      next(); //默认使用这个钩子拦截导航，如果需要进入目标路由，则执行next函数
    })


    router - view
    路由渲染组件属性
      <
      router - view class = 'active'
    name = 'menu' > < /router-view>
    class: '', //路由组件的className
      name: '' //可以给渲染路由加name属性

    router - link API
    to: '/user' //跳转链接目标路径
    tag: 'li' //使用什么标签渲染
    active - class: 'active' //激活时使用的className
    event: '事件名' //使用什么事件激活router-link
    exact: true //路由精确匹配
      : to: {
        name: 路由的name
      } //可以根据路由的name自己生成路径   
      : to: {
        path: 'user',
        params: {
          userId: 1
        }
      } //跳转路径为/user/1   params（动态参数）获取为{userId:1}   
      : to: {
        path: 'user',
        query: {
          userId: 1
        }
      } //跳转路径为/user?userid=1   query可获取为{userId:1}


    transition API
    组件切换过度动画, 使用6个class来控制， class在style用css3 tarnsiton写动画
      <
      transition name = 'left'
    mode = "in-out" >
      <
      router - view > < /router-view> <
      /transition>
    name: 'left' //默认class名已v-开头，如需自定义则在transtion 组件上加name属性
    mode: 'in-out' //切换过度模式，in-out为新元素先进行过度，完成后当前元素过度离开  ***默认
    mode: 'out-in' //切换过度模式,out-in为当前元素先进行过度，完成后新元素过度进入

    v - enter: '' // class类名  定义进入过度的开始状态
    v - enter - to: '' //class类名  定义进入过度的结束状态
    v - enter - active: '' //class类名  定义进入过度活动状态，如过度时间等
    v - leave: '' //class类名   定义离开过度的开始状态
    v - leave - to: '' //class类名  定义离开过度的结束状态
    v - leavee - active: '' //calss类名  定义离开过度活动状态，如过度时间等

    //动态绑定动画案例   左侧的导航往右动画，右侧的导航往左动画
    data() {
        return {
          init: 'left',
        }
      },
      watch: {
        $route(to, from) {
          console.log(to); //目标路由
          console.log(from); //当前路由
          if (to.meta.index < from.meta.index) {
            this.init = 'right'
          } else {
            this.init = 'left'
          }
        }
      }


    Vue 过滤器
    filters: {
      //本组件内使用的过滤器
      a(value) {
        // ……操作
      }
    }
    // 全局过滤器
    Vue.filters(name, function (value) {
      // ……要进行的操作
    })

    用法:
      html结构只能用于 {
        {}
      }
    中 或者v - bind中 {
      {}
    }
    中用法 content | name 用管道符引用
    js中可以像函数一样调用



    Vue插件的使用
    // 在使用自定义封装的函数的时候，有时会需要引入很多个组件，这个时候就可以把封装的函数作为vue的插件引入vue根实例的原型上
    Vue.prototype[name] = [component] //方法一
    /*
        name:$自定义的名字   必须使用$开头     
        component：  需要引入的方法
    */

    // 方法二：
    //插件案例   ***常用写插件（封装方法）办法***
    let local = {
      getuser() {
        console.log('方法一')
      },
      getname() {
        console.log('方法二')
      }
    }
    export default {
      install(vm) {
        vm.prototype.$local = local
      }
    }
    //在main.js文件中注册插件
    Vue.use(local)



    VueRouter + webpack 懒加载
    // 使用vue异步组件
    //写法一：
    /* 
        优点：写起来简便
        缺点：不能多组件合并一个JS文件
    */
    let Login = (resolve) => { //异步组件回调函数
      return import('@/components/login') //使用Import导入组件
    }
    //写法二：
    /* 
        优点：可以根据需求把多个组件合并到一个JS中
        缺点：写起来太麻烦
        require.ensure(依赖，回调，thunk名字)
            参数：
                依赖：array   组件的依赖一般写[]
                回调:回调函数
                thunk:可以根据thunk名字来合并组件，thunk名字相同的会被合并到同一个js文件中
    */
    // 以下例子Login和Main组件被合并在同一个js中
    let Login = (resolve) => {
      return require.ensure([], () => {
        resolve(require('@/components/login'))
      }, 'abc')
    }
    let Main = (resolve) => {
      return require.ensure([], () => {
        resolve(require('@/components/main'))
      }, 'abc')
    }


    Vue router history 模式后台配置
    //详情见 VueRouter 'HTML5 History 模式'


    Vuex
    //安装vuex模块:
    npm install vuex--save
    //作为插件使用：
    Vue.use(Vuex)
    //定义容器：
    var store = new Vuex.Store({
      state: {
        [key]: [value] //key:状态的Key值   value:状态的value值   //全局状态
      },
      mutations: { //这里放的是修改state的事件,只可以写同步操作
        [name](state, data) {
          state[key] = data[key] //state:store.state里的值    data:为组件内部调用时传入的值
        }
      },
      actions: { //actions里为异步操作，异步等待完成后在使用mutations修改状态
        [name](context, data) {
          context.commit([name], data); //使用context.commit触发mutations;
        }
      }
    });
    //注入根实例：
    {
      store,
    }

    //组件中使用：
    export default {
      component: {
        [name]() {
          return this.$store.state[key]; //在计算属性中获取vuex.store中定义的值
        }
      }
    }

    //完整例子:
    //store文件
    import Vue from 'vue'
    import Vuex from 'vuex'

    Vue.use(Vuex)

    var store = new Vuex.Store({
      state: { //状态管理
        conent: 100,
      },
      mutations: { //唯一可以修改state的函数,只可以同步操作
        addMuation(state, data) {
          state.conent += data.n; //第二个参数可以是commit传过来的任意值
        },
        deMuation(state, data) {
          state.conent -= data.de;
        }
      },
      actions: { //异步时把请求写在这里，请求成功后再去提交mutations;
        //写法1：
        addAction(context, data) {
          axios.post('www.baidu.com').then((data) => {
            context.commit('addMuation', data);
          })
        },
        //写法2:使用es6的结构赋值，直接把commit,dispath解构出来  commit触发muation,dispath触发actions
        addAction({
          commit,
          dispath
        }, data) {
          axios.post('www.baidu.com').then((data) => {
            commit('addMuation', data);
          })
        }
      },
      getters: { //Vuex中的计算属性，用法等同于组件中的components
        filterConent(state) {
          return state.conent >= 120 ? 120 : state.conent;
        }
      }

    })

    //组件中
    export default {
      computed: {
        //num可以无限制的望上加
        num() {
          return this.$store.state.conent;
        },
        //num2被限制在了最大120
        num2() {
          return this.$store.getters.filterConent;
        }
      },
      methods: {
        //同步
        add() {
          //触发mutations第一种写法，在同步操作的时候可以直接触发，异步时使用actions
          this.$store.commit('addMuation', {
            n: 5,
          }) //可以传第二个参数过去，自定义的改变状态
        },
        de() {
          //触发mutations第二种写法，在同步操作的时候可以直接触发，异步时使用actions
          this.$store.commit({
            type: 'deMuation',
            de: 10,
          })
        },
        //异步
        add() {
          // 触发actions写法一：
          this.$store.dispatch('addAction', {
            n: 5,
          })
        },
        ad() {
          //触发actions写法二：
          this.$store.dispatch({
            type: 'deMuation',
            de: 10,
          })
        }
      }
    }



    vuex核心概念
    //store:类似容器，包含应用的大部分状态
    /* 
        一个页面只能有一个store
        状态存储时响应式的
        不能直接的修改store中的状态，唯一途径显示提交mutations
    */
    //State:包含所有应用级别状态的对象
    //Getters:组件内部获取store中状态的函数
    //mutations:唯一修改状态的事件回调函数,只可以同步调用
    //actions: 包含异步操作，提交mutations改变状态
    //Module:将store分割成不同的模块

    vuex辅助函数
    // 引入
    import {
      mapState,
      mapGetters,
      mapActions,
      mapMutations
    } from 'vuex'
    //mapState作用：可以直接从vuex的state中取值.
    //mapState 的返回值为一个对象
    //写法一：
    computed: mapState({
      num: state => state.conent, //箭头函数取值，参数为state,返回值为需要取的值
      num: 'conent', //直接取值，value值直接写字符串的需要取的值
      num(state) {
        return state.conent + 100 //函数取值，可以对需要取的值进行二次计算
      },
      conent: 'conent', //在不改变Key值的情况下取值
    })
    //写法二：
    computed: mapState(['conent']) //在mapState中可以传入一个数组，直接key值可以拿到想要的值
    //写法三：   在组件本身有计算属性的情况下
    computed: {
      num() {
        return 123;
      },
      ...mapState(['conent']) //在组件本身有计算属性的情况下，使用...扩展用算符把mapState的返回值与computed原有对象合并 **注意重名**
    }

    //mapGetters  用法与mapState用法想同，取vuex中getters中的值

    //mapActions作用:可以再组件中发起actions，返回值为对象
    // 写法
    methods: {
      ...mapActions({ //使用mapActions方法传入一个对象，key值为定义的事件名，value为vuex中action的名字
        add: 'addAction' //返回值为一个对象，使用...扩展运算符与原有方法合并
      }) //如果actions或者mutations需要传参的话，可以再add函数调用的位置传入参数
    }
    //mapMuations用法与mapActions用法相同




    // 坑
    /* 
        @keydown 不能绑在焦点元素上 绑上去只有元素获得焦点，才能触发
        解决办法：用原声在钩子中自己写，绑定在document上
     */


    //  移动端需要用到的npm依赖
    babel - runtime //辅助编译ES6   生产环境

    fastclick //解决移动端300ms点击延迟   生产环境
    // 用法
    import fastclick from 'fastclick'
    fastclick.attach(document.body)
    babel - polyfill //辅助编译ES6    开发环境
    // 用法：
    import 'babel-polyfill'
  </script>
</body>

</html>